#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
# @version: v1.0
# @author : wlis
# @Email : 1259319710@qq.com
# @Project : g-carbon-bio
# @File : custom_strategy.py
# @Software: PyCharm
# @time: 2025/2/3 20:04
# @description : 用户自定义策略
"""

import datetime
from typing import List, Tuple

import akshare as ak

from service.models.stock_fund_flow_industry_data import StockFundFlowIndustryData
from service.models.stock_individual import StockIndividual
from service.models.stock_individual_fund_flow import StockIndividualFundFlow
from service.models.stock_trade import StockTrade
from service.trade_date_calendar import TradeDateCalendar
from strategies.historical_stockdata_fetcher import HistoricalStockDataFetcher
from strategies.stock_bidask_fetcher import StockBidAskFetcher


class CustomStrategy:

    @classmethod
    def get_filtered_stocks(
            cls,
            stock_df,
            stock_all: List,
            market: str = 'sh'
    ) -> Tuple[List[StockIndividual], List[StockTrade]]:
        """
        获取符合条件的个股和推荐股票的列表。

        :param stock_df: 包含股票数据的DataFrame
        :param stock_all: 所有股票数据的列表
        :param market: 市场类型 ('sh', 'sz', 'bj')
        :return: 返回一个元组，包含符合条件的个股列表和推荐股票列表
        """
        # 创建交易日历、历史股票数据和买卖盘数据的获取器实例
        trade_calendar = TradeDateCalendar()
        stock_historical = HistoricalStockDataFetcher()
        bidask_fetcher = StockBidAskFetcher()

        # 获取当前日期，并确定过去30天的交易日期范围
        current_date = datetime.date.today()
        start_date, end_date = trade_calendar.get_start_end_date(current_date.strftime("%Y-%m-%d"), 30)

        # 初始化符合条件的个股和推荐股票的列表
        individual_stocks = []
        recommended_stocks = []

        # 对股票数据进行筛选，获取符合条件的数据
        filtered_df = cls._filter_stocks(stock_df)

        # 遍历筛选后的股票数据
        for _, row in filtered_df.iterrows():
            code = row['代码']  # 获取当前股票的代码

            # 获取股票的历史数据
            historical_data = stock_historical.fetch_stock_data(code, start_date, end_date)

            # 检查是否有足够的历史数据进行分析
            if len(historical_data) >= 4:
                # 处理个股筛选逻辑
                cls._process_individual_stocks(
                    row, code, historical_data, stock_all, bidask_fetcher, individual_stocks, stock_historical, market
                )

                # 处理推荐股票筛选逻辑
                cls._process_recommended_stocks(
                    row, code, historical_data, stock_all, bidask_fetcher, recommended_stocks, current_date,
                    stock_historical, market
                )

        # 按照涨跌幅和换手率对个股进行排序
        sorted_individual_stocks = sorted(
            individual_stocks,
            key=lambda x: (x.price_change, x.turnover_rate),
            reverse=True
        )
        return sorted_individual_stocks, recommended_stocks  # 返回排序后的个股和推荐股票

    @staticmethod
    def _filter_stocks(stock_df) -> Tuple:
        """
        筛选出符合条件的股票。

        :param stock_df: 包含股票数据的DataFrame
        :return: 筛选后的DataFrame
        """
        # 过滤掉涨幅已经达到10%（涨停）或涨停的股票
        return stock_df[
            ~stock_df['代码'].str.startswith(('688', '689', '300', '301')) &
            ~stock_df['名称'].str.contains('退市', na=False) &
            (stock_df['涨跌幅'] >= 0) &
            (~stock_df['涨跌幅'].isna()) &
            (stock_df['涨跌幅'] < 10)  # 过滤掉涨跌幅已达到10%的股票
        ]

    @classmethod
    def _process_individual_stocks(cls, row, code, historical_data, stock_all, bidask_fetcher, individual_stocks,
                                   stock_historical, market):
        """
        处理个股筛选逻辑并将符合条件的个股添加到列表中。

        :param row: 当前股票的行数据
        :param code: 股票代码
        :param historical_data: 历史数据
        :param stock_all: 所有股票数据列表
        :param bidask_fetcher: 买卖盘获取器
        :param individual_stocks: 存储符合条件的个股列表
        :param market: 市场类型
        """
        previous_3_days_change = [data.change_rate for data in historical_data[-4:-1]]
        previous_3_days_turnover_rate = [data.turnover_rate for data in historical_data[-4:-1]]
        volumes = [data.volume for data in historical_data[-4:]]

        volumes_decreasing = all(volumes[i] >= volumes[i + 1] for i in range(len(volumes) - 1))
        total_turnover_rate = round(sum(previous_3_days_turnover_rate), 1)

        moving_average_20 = sum(data.close_price for data in historical_data[-20:]) / 20 if len(
            historical_data) >= 20 else None

        if moving_average_20 is not None and row['最新价'] > moving_average_20:
            if all(change < 0 for change in previous_3_days_change) and row['涨跌幅'] > 0:
                outer_disk, inner_disk = bidask_fetcher.get_stock_data(code)
                if outer_disk and inner_disk and outer_disk > inner_disk:
                    amplitudes = stock_historical.calculate_amplitudes(historical_data, row['最新价'])
                    industry = next((st.industry for st in stock_all if str(st.stock_code).zfill(6) == code), None)
                    rating = next((st.rating for st in stock_all if str(st.stock_code).zfill(6) == code), None)

                    fund_flow_data = cls.get_stock_individual_fund_flow(code, market)

                    stock_individual = StockIndividual(
                        code=code,
                        name=row['名称'],
                        latest_price=row['最新价'],
                        price_change=row['涨跌幅'],
                        amplitudes=amplitudes,
                        outer_disk=outer_disk,
                        inner_disk=inner_disk,
                        industry=industry,
                        volume_ratio=row['量比'],
                        turnover_rate=row['换手率'],
                        total_turnover_rate=total_turnover_rate,
                        volumes_decreasing=volumes_decreasing,
                        rating=rating,
                        total_net_inflow_positive=fund_flow_data.total_net_inflow_positive,
                        total_net_inflow_negative=fund_flow_data.total_net_inflow_negative
                    )
                    individual_stocks.append(stock_individual)

    @classmethod
    def _process_recommended_stocks(cls, row, code, historical_data, stock_all, bidask_fetcher, recommended_stocks,
                                    current_date, stock_historical, market):
        """
        处理推荐股票筛选逻辑并将符合条件的推荐股票添加到列表中。

        :param row: 当前股票的行数据
        :param code: 股票代码
        :param historical_data: 历史数据
        :param stock_all: 所有股票数据列表
        :param bidask_fetcher: 买卖盘获取器
        :param recommended_stocks: 存储推荐股票列表
        :param current_date: 当前日期，格式为 datetime.date 对象
        :param stock_historical: 历史股票数据获取器实例
        :param market: 市场类型
        """
        # 当前日期已经是 datetime.date 对象，不需要转换
        current_date_obj = current_date

        # 获取推荐股票的历史数据，包括当天的数据
        historical_data_recommendation = stock_historical.get_days_before(historical_data, 5)

        # 检查历史数据是否存在且包含足够的数据（至少3条）
        if historical_data_recommendation and len(historical_data_recommendation) >= 3:
            # 从历史数据中移除当天的数据（如果存在）
            historical_data_recommendation = [
                data for data in historical_data_recommendation if data.date != current_date_obj
            ]

            # 重新检查是否有足够的数据
            if len(historical_data_recommendation) >= 3:
                # 获取前一天的数据（最后一条）
                previous_data = historical_data_recommendation[-1]
                current_data = next((data for data in historical_data if data.date == current_date_obj), None)

                if current_data:
                    current_volume = current_data.volume  # 当前成交量
                    current_close_price = current_data.close_price  # 当前收盘价
                    current_open_price = current_data.open_price  # 当前开盘价
                    previous_volumes = [data.volume for data in historical_data_recommendation[-3:-1]]

                    yesterday_volume = previous_volumes[-1] if len(previous_volumes) >= 1 else 0
                    day_before_yesterday_volume = previous_volumes[-2] if len(previous_volumes) >= 2 else 0

                    # 计算成交量比率
                    volume_ratio_yesterday = round(current_volume / yesterday_volume, 2) if yesterday_volume > 0 else 0
                    volume_ratio_day_before_yesterday = round(current_volume / day_before_yesterday_volume,
                                                              2) if day_before_yesterday_volume > 0 else 0

                    # 推荐条件
                    if ((volume_ratio_yesterday > 2.5 or volume_ratio_day_before_yesterday > 2.5) and
                            (current_close_price > current_open_price) and
                            (current_close_price > previous_data.close_price)):
                        industry = next((st.industry for st in stock_all if str(st.stock_code).zfill(6) == code), None)
                        rating = next((st.rating for st in stock_all if str(st.stock_code).zfill(6) == code), None)
                        outer_disk, inner_disk = bidask_fetcher.get_stock_data(code)

                        # 获取个股资金流数据
                        fund_flow_data = cls.get_stock_individual_fund_flow(code, market)

                        # 创建推荐股票对象并添加到推荐股票列表中
                        stock = StockTrade(
                            0, code, row['换手率'], row['量比'], outer_disk, inner_disk,
                            row['名称'], row['最新价'], row['涨跌幅'], row['涨跌额'],
                            row['成交量'], row['成交额'], row['振幅'], row['最高'],
                            row['最低'], row['今开'], row['昨收'], row['量比'],
                            row['换手率'], row['市盈率-动态'], row['市净率'],
                            row['总市值'], row['流通市值'], row['涨速'],
                            row['5分钟涨跌'], row['60日涨跌幅'],
                            row['年初至今涨跌幅'], industry, 0,
                            volume_ratio_yesterday, volume_ratio_day_before_yesterday, None, rating,
                            fund_flow_data.total_net_inflow_positive,
                            fund_flow_data.total_net_inflow_negative
                        )

                        # 存储成交量比率
                        stock.volume_multipliers = {
                            'yesterday': volume_ratio_yesterday,
                            'day_before_yesterday': volume_ratio_day_before_yesterday
                        }

                        recommended_stocks.append(stock)  # 添加到推荐股票列表中

    @staticmethod
    def get_stock_individual_fund_flow(stock: str, market: str) -> StockIndividualFundFlow:
        """
        获取个股资金流数据。

        :param stock: 股票代码
        :param market: 市场类型 ('sh', 'sz', 'bj')
        :return: 个股资金流数据的对象
        """
        stock_data_df = ak.stock_individual_fund_flow(stock=stock, market=market)

        total_net_inflow_positive = 0
        total_net_inflow_negative = 0

        for _, row in stock_data_df.iterrows():
            net_inflow_large = row['大单净流入-净额']
            net_inflow_super_large = row['超大单净流入-净额']
            net_inflow_medium = row['中单净流入-净额']
            net_inflow_small = row['小单净流入-净额']

            total_net_inflow_positive += max(net_inflow_large, 0) + max(net_inflow_super_large, 0) + \
                                         max(net_inflow_medium, 0) + max(net_inflow_small, 0)
            total_net_inflow_negative += min(net_inflow_large, 0) + min(net_inflow_super_large, 0) + \
                                         min(net_inflow_medium, 0) + min(net_inflow_small, 0)

        return StockIndividualFundFlow(
            date=datetime.date.today(),  # 使用今天的日期
            closing_price=0,  # 可以根据实际情况设置
            price_change=0,  # 可以根据实际情况设置
            net_inflow_large=0,  # 需要根据具体情况设置
            proportion_inflow_large=0,  # 需要根据具体情况设置
            net_inflow_super_large=0,  # 需要根据具体情况设置
            proportion_inflow_super_large=0,  # 需要根据具体情况设置
            net_inflow_medium=0,  # 需要根据具体情况设置
            proportion_inflow_medium=0,  # 需要根据具体情况设置
            net_inflow_small=0,  # 需要根据具体情况设置
            proportion_inflow_small=0,  # 需要根据具体情况设置
            total_net_inflow_positive=total_net_inflow_positive,
            total_net_inflow_negative=total_net_inflow_negative
        )

    @staticmethod
    def get_stock_fund_flow_industry_data() -> List[StockFundFlowIndustryData]:
        """
        获取行业资金流数据。

        :return: 返回包含行业资金流数据的 StockFundFlowIndustryData 对象列表
        """
        stock_fund_flow_industry_list = []
        stock_fund_flow_industry_df = ak.stock_fund_flow_industry(symbol="即时")

        for index, row in stock_fund_flow_industry_df.iterrows():
            if row["净额"] > 0 and row["行业-涨跌幅"] > 0:
                data = StockFundFlowIndustryData(
                    index=row["序号"],
                    industry=row["行业"],
                    industry_index=row["行业指数"],
                    industry_change=row["行业-涨跌幅"],
                    fund_inflow=row["流入资金"],
                    fund_outflow=row["流出资金"],
                    net_amount=row["净额"],
                    company_count=row["公司家数"],
                    leading_stock=row["领涨股"],
                    leading_stock_change=row["领涨股-涨跌幅"],
                    current_price=row["当前价"]
                )
                stock_fund_flow_industry_list.append(data)

        sorted_data = sorted(stock_fund_flow_industry_list, key=lambda x: (x.net_amount, x.industry_change),
                             reverse=True)
        return sorted_data


# 调用 CustomStrategy 获取符合条件的个股和推荐股票
if __name__ == "__main__":
    from service.stock_data_manager import StockDataManager

    stock_df = ak.stock_sh_a_spot_em()
    stock_data_manager = StockDataManager()
    stock_all = stock_data_manager.read_stock_data()

    individual_stocks, recommended_stocks = CustomStrategy.get_filtered_stocks(stock_df, stock_all, "sh")

    print("符合条件的个股列表:")
    for stock in individual_stocks:
        print(stock)

    print("\n推荐股票列表:")
    for stock in recommended_stocks:
        print(stock)

    industry_fund_flow_data = CustomStrategy.get_stock_fund_flow_industry_data()

    print("\n行业资金流数据:")
    for industry_data in industry_fund_flow_data:
        print(industry_data)
